package com.n.passwordbook.PasswordBook;

import com.n.passwordbook.LaunchWindow.TmpDataHelper;
import com.n.passwordbook.MainWindow.CustomTextFieldTableCell;
import com.n.passwordbook.Utils.CryptographyHelper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import org.apache.commons.csv.CSVFormat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.TimeZone;

//PasswordBook的第一行为用户名,第二行为加密后用户名,第三行为上次保存时间，第四行暂时保留
public class PasswordBook
{
    public static String PasswordBookPath;
    public static String UserName;//明文存储
    private static String lastSaveTime;
    public static ObservableList<PasswordStruct> Passwords;
    public static byte[] PBKey;

    public static final int ReservedLineCount = 4;

    public static String getLastSaveTime()
    {
        return CryptographyHelper.decryptStringByAES256(lastSaveTime, PBKey);
    }

    public static boolean loadPasswordBook(String path)
    {
        File passwordBook = new File(path);
        if (passwordBook.exists())
        {
            UserName = getUserNameFromFile(new File(path));
            return UserName != null;
        }
        else
        {
            return false;
        }
    }

    public static boolean checkIfPasswordBookPathValid(File passwordBookFile, String password)
    {
        if (passwordBookFile.exists())
        {
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(passwordBookFile), StandardCharsets.UTF_8)))
            {
                String plaintextUserName = bufferedReader.readLine();
                String encryptedUserName = bufferedReader.readLine();

                return plaintextUserName.equals(CryptographyHelper.decryptStringByAES256(encryptedUserName, CryptographyHelper.convertPasswordToKey(password)));
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return false;
    }

    public static void setPasswordBook(String password, String userName, String path)
    {
        CryptographyHelper.setPBKey(password);
        UserName = userName;
        PasswordBookPath = path;
        Passwords = FXCollections.observableArrayList(new ArrayList<PasswordStruct>());
    }

    public static String savePasswordBook()
    {
        return savePasswordBook(new File(PasswordBookPath));
    }

    public static String savePasswordBook(File path)
    {
        try
        {
            String crlf = System.getProperty("line.separator");
            if (!path.exists())
            {
                path.createNewFile();
            }
            OutputStreamWriter saver = new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8);
            String ret = writeBaseInfo(saver, crlf);

            for (PasswordStruct ps : Passwords)
            {
                saver.write(ps.getName(PBKey) + crlf);//此处才加密name
                saver.write(ps.getPassword() + crlf);
                saver.write(ps.getRemark() + crlf);
            }
            saver.close();
            return ret;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }
    }

    private static String writeBaseInfo(OutputStreamWriter saver, String crlf) throws IOException
    {
        saver.write(UserName + crlf);
        saver.write(CryptographyHelper.encryptStringByAES256(UserName, PBKey) + crlf);
        synchronized (TimeZone.class)
        {
            TimeZone.setDefault(null);
            System.setProperty("user.timezone", "");

            var timeZone = TimeZone.getDefault();
            String localDateTime = timeZone.getDisplayName() + ' ' + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            saver.write(CryptographyHelper.encryptStringByAES256(localDateTime, PBKey) + crlf);
            saver.write(crlf);
            return localDateTime;
        }
    }

    public static void setPasswordBook(String password, String path)
    {
        CryptographyHelper.setPBKey(password);
        PasswordBookPath = path;
        UserName = getUserNameFromFile(new File(path));
        Passwords = FXCollections.observableArrayList(new ArrayList<PasswordStruct>());
        TmpDataHelper.clearAllData();
    }

    private static String getUserNameFromFile(File path)

    {
        if (path.exists())
        {
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8)))
            {
                return bufferedReader.readLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
                return "";
            }
        }
        return "";
    }

    public static boolean readPasswordBook()
    {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(PasswordBookPath)), StandardCharsets.UTF_8)))
        {
            readBaseInfo(reader);
            PasswordStruct.clearInitializeNO();
            String name, password, remark;
            while ((name = reader.readLine()) != null)
            {
                password = reader.readLine();
                if (password == null)
                {
                    Passwords = FXCollections.observableArrayList(new ArrayList<PasswordStruct>());
                    System.gc();
                    return false;
                }
                remark = reader.readLine();
                if (remark == null)
                {
                    Passwords = FXCollections.observableArrayList(new ArrayList<PasswordStruct>());
                    System.gc();
                    return false;
                }
                Passwords.add(new PasswordStruct(CryptographyHelper.decryptStringByAES256(name, PBKey), password, remark, 0));
            }
            return true;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return false;
        }
    }

    public static String exportToCSV(File path)
    {
        try
        {
            if (!path.exists())
            {
                path.createNewFile();
            }
            var writer = new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8);
            var csvPrinter = CSVFormat.EXCEL.withHeader("网站名（或应用名）", "密码", "备注").print(writer);
            for (var password : Passwords)
            {
                csvPrinter.printRecord(password.getName(), password.getPassword(PBKey), password.getRemark(PBKey));
            }
            csvPrinter.flush();
            csvPrinter.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        synchronized (TimeZone.class)
        {
            TimeZone.setDefault(null);
            System.setProperty("user.timezone", "");

            var timeZone = TimeZone.getDefault();
            String localDateTime = timeZone.getDisplayName() + ' ' + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return localDateTime;
        }
    }

    private static void readBaseInfo(BufferedReader reader) throws IOException
    {
        UserName = reader.readLine();
        reader.readLine();//加密后用户名
        lastSaveTime = reader.readLine();
        reader.readLine();//保留行
    }

    public static boolean checkPassword(String password)
    {
        byte[] newKey = CryptographyHelper.convertPasswordToKey(password);
        return Arrays.equals(PBKey, newKey);
    }

    public static void changePBKey(String newPassword)
    {
        byte[] pbKey = CryptographyHelper.convertPasswordToKey(newPassword);
        var operatedPasswords = FXCollections.observableArrayList(Passwords);
        operatedPasswords.parallelStream().forEach(password -> {
            password.setPassword(password.getPassword(PBKey), pbKey);
            password.setRemark(password.getRemark(PBKey), pbKey);
        });
        Passwords = operatedPasswords;
        CryptographyHelper.setPBKey(newPassword);
    }

    public static boolean backupKey(String plainText, String userName, String path)
    {
        try (OutputStreamWriter saver = new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8))
        {
            saver.write("此文件中保存了您的登录用密码明文，应当妥善保管，比如使用文字记录或者在网络上备份\n");
            saver.write("密码：" + plainText + '\n');
            saver.write("密码本用户名：" + userName + '\n');
            synchronized (TimeZone.class)
            {
                TimeZone.setDefault(null);
                System.setProperty("user.timezone", "");

                var timeZone = TimeZone.getDefault();
                String localDateTime = timeZone.getDisplayName() + ' ' + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

                saver.write("保存时间：" + localDateTime);
            }
            return true;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return false;
        }
    }

    public static void resetTableView(TableView<PasswordStruct> tableView)
    {
        {
            //显示账号
            TableColumn<PasswordStruct, String> tmp = (TableColumn<PasswordStruct, String>) tableView.getColumns().get(0);
            tmp.setCellFactory(TextFieldTableCell.forTableColumn());
            tmp.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
            tmp.setOnEditCommit(e -> {
                int index = e.getTablePosition().getRow();
                e.getTableView().getItems().get(index).setName(e.getNewValue());
            });
        }
        {
            //显示密码
            TableColumn<PasswordStruct, String> tmp = (TableColumn<PasswordStruct, String>) tableView.getColumns().get(1);
            tmp.setCellValueFactory(cellData -> cellData.getValue().passwordProperty());
            tmp.setCellFactory(passwordStructStringTableColumn -> new CustomTextFieldTableCell<>());
        }
        {
            //显示备注
            TableColumn<PasswordStruct, String> tmp = (TableColumn<PasswordStruct, String>) tableView.getColumns().get(2);
            tmp.setCellValueFactory(cellData -> cellData.getValue().remarkProperty());
            tmp.setCellFactory(passwordStructStringTableColumn -> new CustomTextFieldTableCell<>());
        }
        {
            tableView.getSelectionModel().select(tableView.getItems().size() - 1);
        }
    }
}

